/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010, 2010

    This file is part of Maxsi Engine.

    Maxsi Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiEngineContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiEngineContributors.h. For more information please see
	MaxsiEngineContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiEngine.dll
	A linkable version of Maxsi Engine

	MaxsiList.cpp
	A system for handling char* based lists

****************************************************************************/

#include "MaxsiEngine.h"

#ifndef __MINGW_H // Hacks to make Maxsi Engine compile using MinGW
#pragma warning(disable: 4267) // Perhaps rewrite the code so it never makes a warning in the first place?
#endif

#ifdef	_DEBUG
#define CRASH() delete (char*)"CRASHME!"
#else
#define CRASH()
#endif

namespace MaxsiEngine
{
	List AddListEntry(List list, char* Buffer, size_t BufferSize)
	{
		if (!list)
		{
				list			=		CreateList(0);
		}

		size_t	ListSize		=		GetListSize(list);

		SetListSize(list,GetListSize(list)+1);
		SetListEntry(list,(size_t)ListSize,Buffer,BufferSize);

		return list;
	}

	LINK	char*			BufferListToBuffer(List BufferList)
	{
		size_t	BufferListSize		=	GetListSize(BufferList);
		size_t	TotalBufferSize		=	0;

		for (size_t N = 0; N < BufferListSize; N++)
		{
			TotalBufferSize+=GetListEntrySize(BufferList,N);
		}

		char* TotalBuffer = new char[TotalBufferSize+1];
		size_t Offset = 0;
		for (size_t N = 0; N < BufferListSize; N++)
		{
			char*	Data		=	(char*)	GetListEntry	(BufferList,(size_t)N);
			size_t	Data_Size	=			GetListEntrySize(BufferList,(size_t)N);
			memcpy(TotalBuffer+Offset,Data,Data_Size);
			Offset+=Data_Size;
		}
		TotalBuffer[Offset] = 0;

		return TotalBuffer;
	}

	LINK	List BuildList(size_t NumParameters, ...)
	{
		List	Out				=	CreateList(NumParameters);
		va_list param_pt;

		va_start(param_pt,NumParameters); // Call the setup macro
		
		char*	Buffer			=	0;

		for (size_t I = 0 ; I < NumParameters ; I++)
		{
			Buffer = va_arg(param_pt,char*);
			if ( Buffer )
			{
				SetListEntry(Out,I,Buffer,strlen(Buffer));
			}
		}

		return Out;
	}

	LINK	List BuildListEx(size_t NumParameters, ...)
	{
		List	Out				=	CreateList(NumParameters);
		va_list param_pt;

		va_start(param_pt,NumParameters); // Call the setup macro

		char*	Buffer			=	0;
		size_t	BufferLenght	=	0;
		

		for (size_t I = 0 ; I < NumParameters ; I++)
		{
			Buffer			=	va_arg(param_pt,char*);
			BufferLenght	=	va_arg(param_pt,size_t);
			
			SetListEntry(Out,I,Buffer,BufferLenght);			
		}

		return Out;
	}

	LINK	List			CreateList(size_t Size)
	{
#ifdef DYNAMIC_LISTS
		List	list_pointer = new ListC;
		*list_pointer = CreateListC(Size);
		return list_pointer;
#else
		size_t list_Len = sizeof(size_t)+Size*(sizeof(size_t)+sizeof(char*));
		List list = new char[list_Len];
		memset(list,0,list_Len);
		memcpy(list,&Size,sizeof(Size));
		return list;
#endif
	}

#ifdef DYNAMIC_LISTS
	LINK	ListC			CreateListC(size_t Size)
	{
		size_t list_Len = sizeof(size_t)+Size*(sizeof(size_t)+sizeof(char*));
		ListC list = new char[list_Len];
		memset(list,0,list_Len);
		memcpy(list,&Size,sizeof(Size));
		if ( GetListSize(&list) != Size )
		{
			CRASH();
		}
		return list;
	}
	// Makes a longer / shorter index buffer, copies as many pointers to it as possible from the old list,
	// and changes the ListC pointer to the new index buffer. Or something clever.
	LINK	bool			SetListSize(List list, size_t NewSize)
	{

		size_t	OldSize		=	GetListSize(list);
		ListC	OldContent	=	*list;
		ListC	NewContent	=	CreateListC(NewSize);
		size_t	MinSize		=	(OldSize > NewSize) ? NewSize : OldSize;
		size_t	EntrySize	=	0;
		void*	Pointer		=	0;

		// Copy parts of the index
		if ( MinSize )
		{
			for (size_t N = 0; N < MinSize; N++)
			{
				EntrySize = GetListEntrySize(list,N);
				Pointer	= GetListEntry(list,N);
				memcpy(NewContent+sizeof(size_t)+N*(sizeof(size_t)+sizeof(char*)),&EntrySize,sizeof(EntrySize));
				memcpy(NewContent+sizeof(size_t)+sizeof(size_t)+N*(sizeof(char*)+sizeof(size_t)),&Pointer,sizeof(Pointer));
			}
		}
		// Delete the contents of the now-forgotten pointers
		if ( OldSize > NewSize )
		{
			for (size_t N = NewSize; N < OldSize; N++)
			{
				EntrySize = GetListEntrySize(list,N);
				Pointer	= GetListEntry(list,N);			
				if (EntrySize) // Buffer controlled by the list
				{
					delete[] (char*)Pointer;
				}
				else	// A random pointer!
				{
					delete (char*)Pointer;
				}
			}
		}
		*list	=	NewContent;
		delete[]	(char*)OldContent;
		return true;
	}
#endif
	LINK	bool			DeleteList(List list)
	{
		if ( !list) { return true; }
		size_t ListSize = GetListSize(list);
		if (ListSize > 0)
		{
			for (size_t I = 0; I < ListSize; I++)
			{
				if ( GetListEntrySize(list,(size_t)I) > 0 )
				{
					void* ToDelete = GetListEntry(list,(size_t)I);
					if ( ToDelete ) { delete[] (char*)ToDelete; }
				}
				else
				{
					void* ToDelete = GetListEntry(list,(size_t)I);
					if ( ToDelete ) { delete   (char*)ToDelete; }
				}
			}
		}
#ifdef DYNAMIC_LISTS
		ListC list_content = *list;
		delete[]	list_content;
		delete		list;
#else
		delete[]	list;
#endif
		return true;
	}

	LINK	void*			GetListEntry(List list, size_t Number)
	{
		if (Number>GetListSize(list)) { return 0; }
		void* Return;
#ifdef DYNAMIC_LISTS
		void* Source = *list+sizeof(size_t)+sizeof(size_t)+Number*(sizeof(char*)+sizeof(size_t));
#else
		void* Source = list+sizeof(size_t)+sizeof(size_t)+Number*(sizeof(char*)+sizeof(size_t));
#endif
		memcpy(&Return,Source,sizeof(Return));

		return Return;
	}

	LINK	bool			SetListEntry(List list, size_t Number, char* Buffer, size_t BufferSize)
	{
		if (Number>GetListSize(list))
		{
			CRASH();
			return false;
		}
		if ( BufferSize == 0 )
		{
#ifdef DYNAMIC_LISTS
			memcpy(*list+sizeof(size_t)+Number*(sizeof(size_t)+sizeof(char*)),&BufferSize,sizeof(BufferSize));
			memcpy(*list+sizeof(size_t)+sizeof(size_t)+Number*(sizeof(char*)+sizeof(size_t)),&Buffer,sizeof(Buffer));
#else
			memcpy(list+sizeof(size_t)+Number*(sizeof(size_t)+sizeof(char*)),&BufferSize,sizeof(BufferSize));
			memcpy(list+sizeof(size_t)+sizeof(size_t)+Number*(sizeof(char*)+sizeof(size_t)),&Buffer,sizeof(Buffer))
#endif
		}
		else
		{
			char* TMP = new char[BufferSize];
			memcpy(TMP,Buffer,BufferSize);
#ifdef DYNAMIC_LISTS
			memcpy(*list+sizeof(size_t)+Number*(sizeof(size_t)+sizeof(char*)),&BufferSize,sizeof(BufferSize));
			memcpy(*list+sizeof(size_t)+sizeof(size_t)+Number*(sizeof(char*)+sizeof(size_t)),&TMP,sizeof(TMP));
#else
			memcpy(list+sizeof(size_t)+Number*(sizeof(size_t)+sizeof(char*)),&BufferSize,sizeof(BufferSize));
			memcpy(list+sizeof(size_t)+sizeof(size_t)+Number*(sizeof(char*)+sizeof(size_t)),&TMP,sizeof(TMP));
#endif
		}
#ifdef	_DEBUG
		// detect any faults
		size_t Sizy = GetListEntrySize(list,Number);
		if (Sizy != BufferSize)
		{
			// crash!
			delete (char*)"CRASHME!";
		}
#endif
		return true;
	}

	LINK	size_t			GetListSize(List list)
	{
		if (!list) { return 0; }
		size_t Sizy = 0;
		memcpy(&Sizy,*list,sizeof(Sizy));
		return Sizy;
	}

	LINK	size_t			GetListEntrySize(List list, size_t Number)
	{
		if (Number>GetListSize(list)) { return 0; }
		size_t Sizy = 0;
		memcpy(&Sizy,*list+sizeof(size_t)+Number*(sizeof(size_t)+sizeof(char*)),sizeof(Sizy));
		return Sizy;
	}

	LINK	List			DuplicateList(List list)
	{
		size_t	EntrySize	=	0;
		size_t	ListSize	=	GetListSize(list);
		List	NewList		=	CreateList(ListSize);
		if ( ListSize )
		{
			for (size_t N = 0; N < ListSize; N++)
			{
				EntrySize = GetListEntrySize(list,N);
				SetListEntry(NewList,N,(char*)GetListEntry(list,N),EntrySize);				
			}
		}
		return NewList;
	}
}
